import copy
import datetime
import os
import re
import time
from uuid import UUID

import pandas as pd
import cn2an
import ast

from module.condition_classifier.DataProcess.JsonExtract import load_file


def get_current_week(date=None):
    """
    获取指定日期所在的周的周一和周日的日期，返回（周一，周日）元组形式
    date: str(2022-12-10)
    :return:
    """
    if date:
        duty_date = datetime.datetime.strptime(str(date), '%Y-%m-%d')
        monday, sunday = duty_date, duty_date
    else:
        monday, sunday = datetime.date.today(), datetime.date.today()
    one_day = datetime.timedelta(days=1)
    while monday.weekday() != 0:
        monday -= one_day
    while sunday.weekday() != 6:
        sunday += one_day

    # return monday, sunday
    # 返回时间字符串
    return datetime.datetime.strftime(monday, "%Y-%m-%d"), datetime.datetime.strftime(sunday, "%Y-%m-%d")


def get_dates(start_date, end_date):
    dates = list()
    start_date = datetime.datetime.strptime(start_date, '%Y-%m-%d')
    end_date = datetime.datetime.strptime(end_date, '%Y-%m-%d')
    dates.append(start_date.strftime('%Y-%m-%d'))
    while start_date < end_date:
        start_date += datetime.timedelta(days=1)
        dates.append(start_date.strftime('%Y-%m-%d'))
    return dates


def uuid4():
    """Generate a random UUID."""
    return UUID(bytes=os.urandom(16), version=4)


class Classifier:
    def __init__(self, metaDict):
        self.rows = pd.DataFrame(load_file(metaDict)[1]['rows'])
        self.equal_to = r'等于|相等|达到'
        self.not_equal = r'不等于'
        self.greater_than = r'大于|超过|高于|多于|(比(.)+(大|高|多))'
        self.smaller_than = r'小于|低于|少于|(比(.)+(小|低|少))'
        self.day_id = r"AUGMENTED_DATASET_LEVEL.I8a8ae5ca0178549554951b9501785cefe3f00058.MT_DIMENSION_FIELD.02de0a83c07e6c06b5f6dc8d09f9fce7-C_DATE_Day-LEVEL-1648634671682"

    def classification(self, question, sel, agg, limit, measure, likes, col_dict):
        """
        主函数
        dim_dict: 是用户传回来的匹配模糊维度/指标的精准维度/指标，形如{"华南大区": "一级部门", "广州茂川科技有限公司": "客户名称","金额": "合同金额"}
        """
        '''
        前处理
        '''
        # # 时间前处理
        # if not re.search(r'(\d+年|历年|往年|每年|各年)', question):
        #     question = "{}".format(datetime.now().year) + question
        # ner_output = self.NER.parse(question)

        # question前处理，将模糊指标名称精确化
        if col_dict != None:
            uid_values = []
            for key, value in col_dict.items():
                for col_head in sel:
                    if value == col_head['name']:
                        if col_head['id'] in measure:
                            temp = {'uuid': str(uuid4()), 'value': value}
                            uid_values.append(temp)
                            question = question.replace(key, temp['uuid'])
                            break
            # 正式替换，避免出现"广州分部金额大于十万的合同数量" -> "广州分部合同个数金额大于十万的合同个数数量"
            for item in uid_values:
                question = question.replace(item['uuid'], item['value'])

        # 对维度识别模块输出的like模糊查询进行格式化
        like_conds = []
        temp_idx = []
        for like in likes:
            if like['id'] not in temp_idx:
                temp = [like['id'], 'in', [like['literal']]]
                temp_idx.append(like['id'])
                like_conds.append(temp)
            else:
                idx = temp_idx.index(like['id'])
                like_conds[idx][2].append(like['literal'])

        conds, having, cond_conn_op, conds_copy = self.conds_and_having_Classifier(question, sel, agg, measure,
                                                                                   like_conds)
        group_by = self.group_by_Classifier(question, sel, agg, measure)
        order_by = self.order_by_Classifier(question, sel, conds, having, measure)
        sqls = []

        # 初始输出
        sql = {}
        sql['question'] = question
        sql['sel'] = sel
        sql['agg'] = agg
        sql['measure'] = measure
        sql['limit'] = limit
        sql['conds'] = conds
        sql['cond_conn_op'] = cond_conn_op
        sql['having'] = having
        sql['group_by'] = group_by
        sql['order_by'] = order_by
        sqls.append(sql)
        # 维度拆分
        for item in conds_copy:
            sql = {}
            temp_sel, temp_conds = self.replaceContradictionConds(item, conds, sel)
            sql['question'] = question
            sql['sel'] = temp_sel
            sql['agg'] = agg
            sql['measure'] = measure
            sql['limit'] = limit
            sql['conds'] = temp_conds
            sql['cond_conn_op'] = cond_conn_op
            sql['having'] = having
            sql['group_by'] = group_by
            sql['order_by'] = order_by
            sqls.append(sql)

        return sqls
        # 打印
        # for _sql in sqls:
        #     self.print_sql(question, _sql)

    def print_sql(self, sql):
        formatter = "Question:{};\n\nsel:\n{}\nagg:\n{}\nmeasure:\n{}\nlimit:\n{}\nconds:\n{}\ncond_conn_op:\n{}\nhaving:\n{}\ngroup_by:\n{}\norder_by:\n{}\n"
        question = sql['question']
        sel = sql['sel']
        agg = sql['agg']
        measure = sql['measure']
        limit = sql['limit']
        conds = sql['conds']
        cond_conn_op = sql['cond_conn_op']
        having = sql['having']
        group_by = sql['group_by']
        order_by = sql['order_by']
        print(formatter.format(question, sel, agg, measure, limit, conds,
                               cond_conn_op, having,
                               group_by, order_by))

    def findDimensionContradiction(self, id):
        '''
        找到当前维度id的矛盾维度id（尚未完成）
        :param id:
        :return:
        '''
        contradict_id = ""
        contradict_dict = {
            'AUGMENTED_DATASET_FIELD.I8a8ae5ca0178549554951b9501785cefe3f00058.MT_DIMENSION_FIELD.0c2bf3603bcd7cd295e19909c11b78fe': 'AUGMENTED_DATASET_FIELD.I8a8ae5ca0178549554951b9501785cefe3f00058.MT_DIMENSION_FIELD.2b4ff0f606441075f12c1be466e3642e',
            'AUGMENTED_DATASET_FIELD.I8a8ae5ca0178549554951b9501785cefe3f00058.MT_DIMENSION_FIELD.2b4ff0f606441075f12c1be466e3642e': 'AUGMENTED_DATASET_FIELD.I8a8ae5ca0178549554951b9501785cefe3f00058.MT_DIMENSION_FIELD.0c2bf3603bcd7cd295e19909c11b78fe'
        }
        if id in contradict_dict.keys():
            contradict_id = contradict_dict[id]
        return contradict_id

    def replaceContradictionConds(self, dimension, conds, sel):
        """
        替换矛盾的id，返回重新构造的conds
        :param dimension:要替换的维度[id,opt,literal]
        :param conds:原本的conds
        :param sel:原本的sel
        :return: new_conds: 替换后的conds和替换
        """
        id = dimension[0]
        contradict_id = self.findDimensionContradiction(id)
        # 构造新的conds
        new_conds = copy.deepcopy(conds)
        for index, item in enumerate(new_conds):
            if contradict_id == item[0]:
                del new_conds[index]
                new_conds.append(dimension)
                break
        # 构造新的sel
        new_sel = copy.deepcopy(sel)
        for index, item in enumerate(new_sel):
            if contradict_id == item['id']:
                del new_sel[index]
        return new_sel, new_conds

    def conds_and_having_Classifier(self, question, sel, agg, measure, like_conds):
        """
        conds及having字段识别模块 - 字面量识别
        like_conds 形如[['id_1','in',['北京','广州']],['id_2','in',['腾讯']]]
        :return: {‘conds': [],'having': [], 'cond_conn_op':'' or [],'conds_copy':[]}
        """

        conds = []
        having = []
        cond_conn_op = ""
        conds_num = 0
        # 存储复制的conds副本
        conds_copy = []
        conds_dimension_ids = []  # 存储添加到conds字段的矛盾维度id
        like_idxs = [item[0] for item in like_conds]
        opt_num_span = self.getNumLiteralList(question)
        temp_measures = []
        time_conds = []  # 时间维度相关的conds，之后过滤time_conds筛选出最小单位时间加入conds中
        # 周识别
        week_cond = self.weekReg(question)
        if len(week_cond['value']) != 0:
            # 进行temp_conds的添加操作
            time_conds.append(week_cond)
        '''
        字符串匹配
        '''
        for index, col_head in enumerate(sel):
            # 操作符 (==/</>/<>/in)
            opt = "=="
            # 字面量
            literal = ""
            # 硬匹配
            id = col_head['id']
            row_mem = self.rows[(self.rows['id'] == id)]['members']

            # 对于非时间类型、维度类型
            if len(row_mem) != 0:
                literal = []
                if id not in like_idxs:
                    for member_list in row_mem:
                        for member in member_list:
                            if member != "" and member in question:
                                literal.append(member)

                    if len(literal) == 0:
                        # 没有检索到字面量，说明是like模糊查询或question中的只是维度名称
                        # 对于模糊查询的维度
                        continue
                    opt = "==" if len(literal) == 1 else "in"
                    literal = literal[0] if len(literal) == 1 else literal
                    condtradict_id = self.findDimensionContradiction(id)
                    if condtradict_id not in conds_dimension_ids:
                        if agg[index] == "":
                            conds_num += 1
                            conds_dimension_ids.append(id)
                            conds.append([id, opt, literal])
                        else:
                            having.append([id, agg[index], opt, literal])
                    else:
                        # 存在矛盾维度 北方大区和深圳大区的合同金额
                        conds_copy.append([id, opt, literal])
                else:
                    # 该维度属于like模糊查询
                    sim_list = []
                    like_index = like_idxs.index(id)
                    for member_list in row_mem:
                        for member in member_list:
                            for item in like_conds[like_index][2]:
                                if item != "" and item in member:
                                    sim_list.append(member)
                    like_conds[like_index][2] = sim_list
                    condtradict_id = self.findDimensionContradiction(id)
                    if condtradict_id not in conds_dimension_ids:
                        if agg[index] == "":
                            conds_num += 1
                            conds_dimension_ids.append(id)
                            conds.append(like_conds[like_index])
                        else:
                            having.append(like_conds[like_index])
                    else:
                        # 存在矛盾维度 北方大区和深圳大区的合同金额
                        conds_copy.append(like_conds[like_index])

            # 对于时间类型-正则匹配
            elif 'timeLevel' in col_head.keys() and col_head['timeLevel'] != "":
                time = col_head['timeLevel']
                conds_num += 1
                temp_cond = self.timeLiteralRecognition(question, time, id)
                if len(temp_cond['value']) > 0:
                    time_conds.append(temp_cond)
                # 对季度和月的特殊处理，将年的conds合并指月和季度中，同时删除年
                # if time in ["quarter", "month"]:
                #     # 识别出年
                #     id = ""
                #     idx = 0  # 要删除的下标
                #     years = []
                #     for column in sel:
                #         if column['name'] == '年':
                #             id = column['id']
                #             break
                #     for index, item in enumerate(conds):
                #         if id == item[0]:
                #             if isinstance(item[2], list):
                #                 years.extend(item[2])
                #             else:
                #                 years.append(item[2])
                #             idx = index
                #             break
                #     # 合并格式化
                #     smaller_times = temp_cond[2] if isinstance(temp_cond[2], list) else [temp_cond[2]]
                #     t = self.time_dim_merge(years, smaller_times, time)
                #     temp_cond[2] = t[0] if len(t) == 1 else t
                #     del conds[idx]
                # conds.append(temp_cond)

            # 对于指标类型-ner/msra
            elif id in measure:
                m = re.finditer(col_head['name'], question)
                if m:
                    for item in m:
                        temp_measures.append({'id': id, 'agg': agg[index], 'span': item.span()})
                # # 大小写转换
                # num_pattern = r"([^前|后|排名])((\d|\.|一|二|两|三|四|五|六|七|八|九|十|百|千|万|亿)+)(?!\d*(个|)(年|月|季|日))"
                # # num_pattern = r"([^前|后|排名])((\d|\.|一|二|两|三|四|五|六|七|八|九|十|百|千|万|亿)+)"
                # # print(num_pattern)
                # opt = self.operatorClassifier(question)
                # m = re.search(num_pattern, question)
                # if m:
                #     num = float(cn2an.cn2an(m[2], "smart"))
                #     if agg[index] == "":
                #         conds_num += 1
                #         conds.append([id, opt, num])
                #     else:
                #         having.append([id, agg[index], opt, num])

        # 时间维度的合并
        time_conds = self.timeDimMerge(question, time_conds)
        if len(time_conds) > 0:
            conds.extend(time_conds)

        # having指标字面量匹配，根据实体距离判断
        for optNum in opt_num_span:
            smallest_dis = len(question)
            smallest_mes = {}
            smallest_idx = -1
            for idx, mes in enumerate(temp_measures):
                temp = smallest_dis
                if optNum['span'][0] > mes['span'][0]:
                    temp = optNum['span'][0] - mes['span'][1]
                elif optNum['span'][0] < mes['span'][0]:
                    temp = mes['span'][0] - optNum['span'][1]
                if temp < smallest_dis:
                    smallest_dis = temp
                    smallest_mes = mes.copy()
                    smallest_idx = idx

            # 赋值并删除元素
            if smallest_idx != -1:
                mes_id = smallest_mes['id']
                agg = smallest_mes['agg']
                if agg == "":
                    conds_num += 1
                    conds.append([mes_id, optNum['opt'], str(optNum['num'])])
                else:
                    having.append([mes_id, agg, optNum['opt'], str(optNum['num'])])

                del temp_measures[smallest_idx]

        # cond_conn_op 赋值
        # if conds_num > 2:
        #     cond_conn_op = ["and"] * (len(conds) - 1)
        # elif conds_num == 2:
        #     cond_conn_op = "and"
        cond_len = len(conds)
        if cond_len > 2:
            cond_conn_op = ["and"] * (len(conds) - 1)
        elif cond_len == 2:
            cond_conn_op = "and"

        return conds, having, cond_conn_op, conds_copy

    def timeDimMerge(self, question, time_conds):
        """
        时间维度的合并
        :param question: 自然语言问句
        :param time_conds: [{'id':‘1213’,'key':'周','opt':'==','value':[{'span':(0,1),'literal':['2022-11-12']}]]
        :return: 关于时间维度的最终的conds，形如[['id','in',['2012','2013']]/[['id','==','2020Q1']
        """
        res = []
        # 如果只有一个维度
        if len(time_conds) == 1:
            temp = []
            for value in time_conds[0]['value']:
                if type(value['literal']) == list:
                    temp.extend(value['literal'])
                elif type(value['literal']) == str:
                    temp.append(value['literal'])
            # res.append([time_conds[0]['id'], time_conds[0]['opt'], temp])
            # 即使只有一个时间也放到列表中进行统一处理
            res.append([time_conds[0]['id'], 'in', temp])
        elif len(time_conds) > 1:
            '''
            根据时间维度从小到大排序 (day(week) < month < quarter < year)
            '''
            sorted_dict = {'day': None, 'week': None, 'month': None, 'quarter': None, 'year': None}
            for item in time_conds:
                sorted_dict[item['key']] = item

            # 将 day 和 week 进行合并（有个问题是考虑处理前合并还是处理后再合并）
            if sorted_dict['week'] != None:
                if sorted_dict['day'] == None:
                    sorted_dict['day'] = sorted_dict['week']
                else:
                    sorted_dict['day']['value'].extend(sorted_dict['week']['value'])

            '''
            维度合并
            '''
            # (year, quarter)
            if sorted_dict['year'] != None and sorted_dict['quarter'] != None:
                # [{'span','literal'}]
                year = sorted_dict['year']['value']
                quarter = sorted_dict['quarter']['value']
                for item_q in quarter:
                    if type(item_q['literal']) == str:
                        item_q['literal'] = [item_q['literal']]
                    min_year = []  # ['2012','2013']
                    min_distance = len(question)
                    for item_y in year:
                        dis = item_q['span'][0] - item_y['span'][0]
                        # year在month前面
                        if dis > 0 and dis < min_distance:
                            min_distance = dis
                            min_year = item_y['literal'] if type(item_y['literal']) == list else [item_y['literal']]
                    if len(min_year) == 0:
                        min_year = [str(datetime.datetime.now().year)]
                    # 维度合并
                    item_q['literal'] = self.quarterAndMonthMerge(min_year, item_q['literal'], 'quarter')

            # (year, month)
            if sorted_dict['year'] != None and sorted_dict['month'] != None:
                year = sorted_dict['year']['value']
                month = sorted_dict['month']['value']
                for item_m in month:
                    if type(item_m['literal']) == str:
                        item_m['literal'] = [item_m['literal']]

                    min_year = []
                    min_distance = len(question)
                    for item_y in year:
                        dis = item_m['span'][0] - item_y['span'][0]
                        # year在month前面
                        if dis > 0 and dis < min_distance:
                            min_distance = dis
                            min_year = item_y['literal'] if type(item_y['literal']) == list else [item_y['literal']]
                    if len(min_year) == 0:
                        min_year = [str(datetime.datetime.now().year)]
                    # 维度合并
                    item_m['literal'] = self.quarterAndMonthMerge(min_year, item_m['literal'], 'month')

            # (year, day)
            if sorted_dict['year'] != None and sorted_dict['day'] != None:
                year = sorted_dict['year']['value']
                day = sorted_dict['day']['value']

                for item_d in day:
                    for item_y in year:
                        pass
            # ((year, month),day) - 实际上是month和day的合并

            '''
            选择最小维度（year > quarter > month > day, ）
            '''
            if sorted_dict['day'] != None:
                literal = []
                for item in sorted_dict['day']['value']:
                    if type(item['literal']) == list:
                        literal.extend(item['literal'])
                    else:
                        literal.append(item['literal'])
                literal = list(set(literal))
                res.append([sorted_dict['day']['id'], 'in', literal])
            elif sorted_dict['month'] != None:
                literal = []
                for item in sorted_dict['month']['value']:
                    literal.extend(item['literal'])
                literal = list(set(literal))
                res.append([sorted_dict['month']['id'], 'in', literal])
            elif sorted_dict['quarter'] != None:
                literal = []
                for item in sorted_dict['quarter']['value']:
                    literal.extend(item['literal'])
                literal = list(set(literal))
                res.append([sorted_dict['quarter']['id'], 'in', literal])
            elif sorted_dict['year'] != None:
                literal = []
                for item in sorted_dict['year']['value']:
                    literal.extend(item['literal'])
                literal = list(set(literal))
                res.append([sorted_dict['year']['id'], 'in', literal])

        return res

    def getNumLiteralList(self, question):
        """
        识别问句中中的如“大于四百万”、“等于两千”等字面量
        :param question: 自然语言问句
        :return: [{'opt': '>', 'num': '100000', 'span': (4, 7)}]
        """
        # 获取操作符
        equal_to = r'等于|相等|达到|(与|和)(.)+(相同|相符|一样)'
        not_equal = r'不等于|不等'
        greater_than = r'大于|超过|高于|多于|(比(.)+(大|高|多))'
        smaller_than = r'小于|低于|少于|(比(.)+(小|低|少))'
        opt_patterns = {"=": equal_to, "<>": not_equal, ">": greater_than, "<": smaller_than}
        opt_list = []
        for k, pattern in opt_patterns.items():
            m = re.finditer(pattern, question)
            if m:
                for item in m:
                    opt_list.append({'opt': k, 'span': item.span()})

        # 获取数字字面量
        num_list = []
        num_pattern = r"((\d|\.|一|二|两|三|四|五|六|七|八|九|十|百|千|万|亿)+)(?!\d*(个|)(年|月|季|日))"
        limit_many_key_pattern = r'(排名前(\d+)|排名后(\d+)|前(\d+)|后(\d+)|(\d+)个|(\d+)名|top(\d+)|第(\d+))(?!(个|年|月|日|季))'
        # 将limit相关的数字字面量和第几个月这种替换成无意义的占位符
        question = re.sub(limit_many_key_pattern, "[SEP]", question)
        m = re.finditer(num_pattern, question)
        for item in m:
            num = float(cn2an.cn2an(item.group(1), "smart"))
            num_list.append({'num': num, 'span': item.span()})

        # 配对
        res = []
        for opt in opt_list:
            for number in num_list:
                # 如果数字在比较符后面，则匹配（如“大于十万”、“不等于200”）
                if opt['span'][1] == number['span'][0]:
                    res.append({'opt': opt['opt'], 'num': number['num'], 'span': (opt['span'][0], number['span'][1])})
                    break
                # 如果数字在比较复符之间，则匹配（如“和四百万一样”）
                if opt['span'][0] < number['span'][0] and opt['span'][1] > number['span'][1]:
                    res.append({'opt': opt['opt'], 'num': number['num'], 'span': (opt['span'][0], opt['span'][1])})
                    break

        return res

    def group_by_Classifier(self, question, sel, agg, measure):
        """
        group_by字段识别方法
        1. 一般时间维度都包含在group_by
        2. 一般有聚合函数group_by字段才不为空
        :param question:
        :param sel:
        :param agg:
        :param measure:
        :return:
        """
        group_by = []
        # 简单的把没有聚合操作且为维度类型的列头id包含在group_by中
        for index, col_head in enumerate(sel):
            id = col_head['id']
            if agg[index] == "" and id not in measure:
                group_by.append(id)

        return group_by

    def order_by_Classifier(self, question, sel, conds, having, measure):
        """
        order_by字段识别
        1. 一般limit字段不为空的都要进行排序操作
        2. 先对指标进行操作：对有关联操作的指标进行排序
        3. 当没有指标时，对时间维度进行操作：选取单位最小的（天、月、季度、年）
        :param question:
        :param sel:
        :param agg:
        :param measure:
        :return:
        """
        order_by = []
        having_ids = [having_condition[0] for having_condition in having]

        # limit(前x的还没实现)
        limit_many_key_pattern = r"最大|最多|最好|最棒|最高|最厉害|最少|最小|最差|最低|排名最后|(排名前(\d+)|排名后(\d+)|前(\d+)|后(\d+)|(\d+)个|(\d+)名|top(\d+)|第(\d+))(?!(个|年|月|日|季))"
        m = re.search(limit_many_key_pattern, question)
        if m:
            limit_idx_tuple = m.span()
            measure_id = ""
            alias = ""
            min_distance = len(question)
            temp_sequence = "ASC"
            for id in measure:
                name = [item['name'] for item in sel if item['id'] == id][0]
                measure_idx = question.find(name)
                if measure_idx != -1:
                    measure_tuple = (measure_idx, measure_idx + len(name))
                    distance = abs(limit_idx_tuple[0] - measure_tuple[1]) if measure_tuple[0] < limit_idx_tuple[
                        0] else abs(measure_tuple[0] - limit_idx_tuple[1])
                    if distance < min_distance:
                        min_distance = distance
                        alias = name
                        measure_id = id
                        span = set(list(question[limit_idx_tuple[0]:limit_idx_tuple[1]]))
                        ASC_literal = set(["少", "小", "差", "低", "后", "倒"])
                        if len(ASC_literal) > len(ASC_literal - span):
                            # 说明存在这些字眼
                            temp_sequence = "ASC"
                        else:
                            # 说明不存在这些自雅安
                            temp_sequence = "DESC"

            order_by.append([measure_id, temp_sequence, "ROW", "NAME", alias])

        # 检查指标操作
        for id in measure:
            if id in having_ids:
                index = having_ids.index(id)
                opt = having[index][2]
                sequence = "DESC" if opt in [">", ">=", "="] else "ASC"
                alias = ""
                for item in sel:
                    if item['id'] == id:
                        alias = item['name']
                order_by.append([id, sequence, "ROW", "NAME", alias])

        # 检查时间维度操作
        if len(order_by) == 0:
            time_cmp = {'': 4, 'year': 3, 'quarter': 2, 'month': 1, 'day': 0}
            min_time = time_cmp['']
            id = ''
            alias = ''
            for index, col_head in enumerate(sel):
                if 'timeLevel' in col_head.keys() and time_cmp[col_head['timeLevel']] < min_time:
                    min_time = time_cmp[col_head['timeLevel']]
                    id = col_head['id']
                    alias = col_head['name']
            order_by.append([id, "ASC", "COL", "NAME", alias])

        return order_by

    def operatorClassifier(self, question):
        """
        操作符识别（==、<、>、<>）
        :param question: 自然语言问题
        :return:
        """
        opt = "=="

        equal_to = r'等于|相等|达到'
        not_equal = r'不等于'
        greater_than = r'大于|超过|高于|多于|(比(.)+(大|高|多))'
        smaller_than = r'小于|低于|少于|(比(.)+(小|低|少))'

        if re.search(equal_to, question):
            opt = "=="
        elif re.search(not_equal, question):
            opt = "<>"
        elif re.search(greater_than, question):
            opt = ">"
        elif re.search(smaller_than, question):
            opt = "<"

        return opt

    def getDateBefore(self, n):
        """
        # 获取前1天或N天的日期，n=1：前1天；n=N：前N天
        :param n: 前n天，n为非负数
        :return:
        """
        today = datetime.datetime.now()
        # 计算偏差时间
        offset = datetime.timedelta(days=-n)
        # 日期格式化
        finaldata = (today + offset).strftime('%Y-%m-%d')
        return finaldata

    def weekReg(self, question, id=None):
        """
        识别问句中的和“周”相关的条件
        :param question: 自然语言查询
        :return: 与周相关的条件列表或空列表的字典，形如：
        {'key': '周', 'value': [{'span': (4, 6), 'literal': ['2022-12-12', '2022-12-13']}, {'span': (0, 3), 'literal': ['2022-11-07']}]}
        """
        # 正则
        current_week_pattern = r"(本|该|这|这一)(周)"
        past_week_pattern = r"上周|(上|前)(个|)(周|星期)"
        past_n_weeks_pattern = r"(\d+|[一二两三四五六七八九十]+)(个|)(周|星期)(以|)前"
        week_duration_pattern = r"(近|过去|连续|前)(\d+|一|二|两|三|四|五|六|七|八|九|十)(周|星期)"

        # 周字面量存储列表
        week_literals = []
        # 要返回的数据
        res = {"key": "week", "value": week_literals, "id": self.day_id, "opt": "=="}
        # 本周
        m = re.finditer(current_week_pattern, question)
        for item in m:
            temp_days = self.get_week(str(datetime.datetime.now().date()), is_final_day=True)
            week_literals.append({"span": item.span(), "literal": temp_days})

        # 上一周
        m = re.finditer(past_week_pattern, question)
        for item in m:
            past_weekday = self.getDateBefore(7)
            temp_days = self.get_week(past_weekday)
            week_literals.append({"span": item.span(), "literal": temp_days})
        # x周前
        m = re.finditer(past_n_weeks_pattern, question)
        for item in m:
            num = int(cn2an.cn2an(item.group(1), "smart"))
            past_weekday = self.getDateBefore(7 * num)
            temp_days = self.get_week(past_weekday)
            week_literals.append({"span": item.span(), "literal": temp_days})

        # 过去几周
        m = re.finditer(week_duration_pattern, question)
        for item in m:
            temp = []
            num = int(cn2an.cn2an(item.group(2), "smart"))
            for i in range(num):
                past_weekday = self.getDateBefore(i * 7)
                if i == 0:
                    temp_days = self.get_week(past_weekday, True)
                else:
                    temp_days = self.get_week(past_weekday)
                temp.extend(temp_days)
            week_literals.append({"span": item.span(), "literal": temp})

        # 判断opt
        if len(res['value']) > 1 or (len(res['value']) == 1 and len(res['value'][0]['literal']) > 1):
            res['opt'] = "in"

        return res

    def get_week(self, date, is_final_day=False):
        """
        获取具体某天所在星期的七天日期
        :param date: str('2022-11-29')
        :param is_final_day: bool值，True则说明只包含具体某天（包括具体某天）所在的周的前几天
        :return: 形如['2022-11-28', '2022-11-29', '2022-11-30', '2022-12-01', '2022-12-02', '2022-12-03', '2022-12-04']
        """
        if time.strptime(date, "%Y-%m-%d"):
            monday, sunday = get_current_week(date)
            if is_final_day:
                return get_dates(monday, date)
            else:
                return get_dates(monday, sunday)

    def timeLiteralRecognition(self, question, time, id):
        """
        处理时间维度的字面量识别
        :param question: 查询问题
        :param time: 时间维度：year, quarter, month, day
        :param id: 列头id
        :return:
        """
        res = {"key": time, "id": id, "opt": None, "value": None}
        if time == "year":
            current_year = str(datetime.datetime.now().year)
            year_span_pattern = r""
            cur_year_pattern = r"(今|该|本|当)(年)"
            exact_year_pattern = r"((\d{4})|(\d{2}))(年)"
            past_year_pattern = r"(去|前|上)(一|)(年)"
            past_n_year_pattern = r"(\d+|[一二两三四五六七八九十]+)(年)(以|)前"
            year_duration_pattern = r"(近|过去|连续|前)(\d+|一|二|两|三|四|五|六|七|八|九|十)(年)"
            # 年字面量存储列表
            year_literals = []

            # 今年
            m = re.search(cur_year_pattern, question)
            if m:
                year_literals.append({"span": m.span(), "literal": current_year})

            # 去年
            m = re.search(past_year_pattern, question)
            if m:
                year_literals.append({"span": m.span(), "literal": str(int(current_year) - 1)})

            # n年前
            m = re.search(past_n_year_pattern, question)
            if m:
                past_y = int(cn2an.cn2an(m[1], "smart"))
                year_literals.append({"span": m.span(), "literal": str(int(current_year) - past_y)})
            # 具体xxxx年
            m = re.finditer(exact_year_pattern, question)
            for item in m:
                year = item.group(1)
                if len(year) == 2:
                    year = current_year[:2]+year
                temp = year if year <= str(current_year) else current_year
                year_literals.append({"span": item.span(), "literal": temp})

            # 前n年
            m = re.finditer(year_duration_pattern, question)
            # find max number
            temp_max = 0
            span = 0
            for item in m:
                duration = int(cn2an.cn2an(item.group(2), "smart"))
                if temp_max < duration:
                    temp_max = duration
                    span = item.span()

            temp_literal = []
            for t in range(temp_max):
                temp = str(int(current_year) - t)
                temp_literal.append(temp)
            if len(temp_literal) > 0:
                year_literals.append({"span": span, "literal": temp_literal})

            res['value'] = year_literals

            # 将元素的opt确定
            length = len(year_literals)
            if length == 0:
                year_literals.append({"span": (0, 0), "literal": current_year})
                res['opt'] = "=="
            elif length == 1:
                if type(year_literals[0]['literal']) == list and len(year_literals[0]['literal']) > 1:
                    res['opt'] = "in"
                else:
                    res['opt'] = "=="
            else:
                res['opt'] = "in"
            return res
        elif time == "quarter":
            map = {'1': 'Q1', '2': 'Q1', '3': 'Q1', '4': 'Q2', '5': 'Q2', '6': 'Q2', '7': 'Q3', '8': 'Q3', '9': 'Q3',
                   '10': 'Q4', '11': 'Q4', '12': 'Q4'}
            # for i in range(12):
            #     idx = int(i / 3)
            #     map[str(i + 1)] = "Q{}".format(idx + 1)
            current_month = str(datetime.datetime.now().month)
            current_quarter = map[current_month]

            # 本季度
            cur_quarter_pattern = "该季|该季度|本季度|这(一|个)季度"
            # 具体某季度
            Q_num_quarter_pattern = r"(Q|q)(1|2|3|4)"
            exact_num_quarter_pattern = r"(第|)([1-4]|一|二|三|四)(季度)"
            # 前/近n季度
            past_quarter_pattern = r"(前|近|上)([1-4]|一|二|三|四)(季|季度)"
            quarter_literals = []

            # 本季度
            m = re.search(cur_quarter_pattern, question)
            if m:
                quarter_literals.append({"span": m.span(), "literal": current_quarter})

            # 具体某季度
            m = re.finditer(Q_num_quarter_pattern, question)

            for item in m:
                temp = "Q{}".format(item.group(2))
                quarter_literals.append({"span": item.span(), "literal": temp})
            m = re.finditer(exact_num_quarter_pattern, question)
            for item in m:
                q = int(cn2an.cn2an(item.group(2), "smart"))
                temp = "Q{}".format(str(q))
                quarter_literals.append({"span": item.span(), "literal": temp})
            # 前/近n季度
            m = re.search(past_quarter_pattern, question)
            if m:
                cur_q = int(current_month / 3 + 1)
                q = int(cn2an.cn2an(m[2], "smart"))
                if q > 0 and q <= cur_q:
                    t = []
                    for t in range(q):
                        temp = "Q{}".format(str(cur_q - t))
                        if temp not in quarter_literals:
                            t.append(temp)
                    quarter_literals.append({"span": m.span(), "literal": t})

            res['value'] = quarter_literals
            # 将元素返回
            length = len(quarter_literals)
            if length == 0:
                res['opt'] = "=="
            elif length == 1:
                if type(quarter_literals[0]['literal']) == list and len(quarter_literals[0]['literal']) > 1:
                    res['opt'] = "in"
                else:
                    res['opt'] = "=="
            else:
                res['opt'] = "in"
            return res

        elif time == "month":
            # 月
            current_month = str(datetime.datetime.now().month)
            month_duration_pattern = r"(近|过去|连续|前|上)(\d+|一|二|两|三|四|五|六|七|八|九|十|十一|十二)(个|)(月)"
            # exact_month_pattern要放在month_duration_pattern之后！
            exact_month_pattern = r"(\d+|一|二|两|三|四|五|六|七|八|九|十|十一|十二)(月)"
            cur_month_pattern = r"(今|该|本|当|这|这个)(月)"
            month_literals = []
            # 如果是month_duration_pattern，则exact_month_pattern不进行识别
            is_duration = False

            # 本月
            m = re.search(cur_month_pattern, question)
            if m:
                month_literals.append({"span": m.span(), "literal": current_month})

            # 前n月
            m = re.finditer(month_duration_pattern, question)
            temp_max = 0
            span = 0
            for item in m:
                is_duration = True
                duration = int(cn2an.cn2an(item.group(2), "smart"))
                if duration <= current_month and duration > temp_max:
                    temp_max = duration
                    span = item.span()

            temp_literal = []
            for t in range(temp_max):
                temp = str(int(current_month) - t)
                if temp not in month_literals:
                    temp_literal.append(temp)
            if len(temp_literal) > 0:
                month_literals.append({"span": span, "literal": temp_literal})

            # 具体x月
            if not is_duration:
                m = re.finditer(exact_month_pattern, question)
                for item in m:
                    literal = int(cn2an.cn2an(item.group(1), "smart"))
                    if literal > 0 and literal <= 12:
                        pass
                    else:
                        literal = current_month
                    month_literals.append({"span": item.span(), "literal": str(literal)})

            res['value'] = month_literals
            # 将元素返回
            length = len(month_literals)
            if length == 0:
                res['opt'] = "=="
            elif length == 1:
                if type(month_literals[0]['literal']) == list and len(month_literals[0]['literal']) > 1:
                    res['opt'] = "in"
                else:
                    res['opt'] = "=="
            else:
                res['opt'] = "in"
            return res

        elif time == "day":
            # 日
            current_day = str(datetime.datetime.now().date())
            # 当天
            cur_day_pattern = r"(今|当)(天|日)"
            # 昨天
            yesterday_pattern = r"昨(天|日)"
            # 前天
            two_days_ago_pattern = r"([^大]|^)前(天|日)"
            # 大前天
            three_days_ago_pattern = r"大前(天|日)"
            # 前n天
            past_n_day_pattern = r"(前)(\d+|一|二|两|三|四|五|六|七|八|九|十)(天|日)"
            # 近n天
            day_duration_pattern = r"(近|过去|连续)(\d+|一|二|两|三|四|五|六|七|八|九|十)(天|日)"
            # xx号
            exact_date_pattern = r"(年|月)(的|)((\d|一|二|两|三|四|五|六|七|八|九|十)+)(号|日)"
            day_literals = []

            # 当天
            m = re.search(cur_day_pattern, question)
            if m:
                day_literals.append({"span": m.span(), "literal": current_day})

            # 昨天
            m = re.search(yesterday_pattern, question)
            if m:
                day_literals.append({"span": m.span(), "literal": self.getDateBefore(1)})

            # 前天
            m = re.search(two_days_ago_pattern, question)
            if m:
                day_literals.append({"span": m.span(), "literal": self.getDateBefore(2)})

            # 大前天
            m = re.search(three_days_ago_pattern, question)
            if m:
                day_literals.append({"span": m.span(), "literal": self.getDateBefore(3)})

            # 前n天
            m = re.finditer(past_n_day_pattern, question)
            # find max number
            temp_max = 0
            has_iter = False
            span = 0
            for item in m:
                has_iter = True
                duration = int(cn2an.cn2an(item.group(2), "smart"))
                if temp_max < duration:
                    temp_max = duration
                    span = item.span()

            exact_date = self.getDateBefore(temp_max)
            if has_iter:
                day_literals.append({"span": span, "literal": exact_date})

            # 近n天
            m = re.finditer(day_duration_pattern, question)
            temp_max = 0
            has_iter = False
            span = 0
            for item in m:
                has_iter = True
                duration = int(cn2an.cn2an(item.group(2), "smart"))
                if temp_max < duration:
                    temp_max = duration
                    span = item.span()
            temp_literal = []
            for t in range(temp_max):
                exact_date = self.getDateBefore(t)
                temp_literal.append(exact_date)
            if len(temp_literal) > 0:
                day_literals.append({"span": span, "literal": temp_literal})

            # 具体xx号
            m = re.finditer(exact_date_pattern, question)
            for item in m:
                date = int(cn2an.cn2an(item.group(3), "smart"))  # 具体xx号
                if date >= 1 and date <= 31:
                    year = datetime.datetime.now().year
                    month = datetime.datetime.now().month
                    if date < 10:
                        date = "0" + str(date)
                    exact_date = "{}-{}-{}".format(year, month, date)
                else:
                    exact_date = datetime.datetime.now().date()
                day_literals.append({"span": item.span(), "literal": [exact_date]})

            res['value'] = day_literals
            # 将元素返回
            length = len(day_literals)
            if length == 0:
                res['opt'] = "=="
            elif length == 1:
                if type(day_literals[0]['literal']) == list and len(day_literals[0]['literal']) > 1:
                    res['opt'] = "in"
                else:
                    res['opt'] = "=="
            else:
                res['opt'] = "in"
            return res

        return []

    def quarterAndMonthMerge(self, years, smaller_times, mode):
        """
        月、季度时间维度格式化
        :param year: 年列表
        :param smaller_time: 季度或月份列表
        :param mode: 对应的time_level
        :return: 格式化数据
        """
        res = []
        separator = ""
        if mode == "quarter":
            separator = ""
        elif mode == "month":
            separator = "-"
        for year in years:
            for time in smaller_times:
                # 当smaller time为数字且只有一位时进行补零操作（格式化）
                if time.isdigit() and len(time) == 1:
                    time = '0' + time
                res.append(str(year) + separator + str(time))
        return res


# import DataLoader
# if __name__ == '__main__':
#     classifier = Classifier("../../metaData/data_dict.json")
#     loader = DataLoader.DataLoader("./TestData/my_data.csv")
#     data_dict = loader.loadData_csv()
#
#     for data in data_dict:
#         question = data['question']
#         sel = ast.literal_eval(data['sel'])
#         agg = ast.literal_eval(data['agg'])
#         limit = ast.literal_eval(data['limit'])
#         measure = ast.literal_eval(data['measure'])
#         sqls = classifier.classification(question, sel, agg, limit, measure)
#         for sql in sqls:
#             classifier.print_sql(sql)
#         print("---------------")

if __name__ == '__main__':
    classifier = Classifier("../../metaData/data_dict.json")
    # print(classifier.weekReg("前三周广州分部的合同金额"))
    # print(classifier.weekReg("五周前和这周深圳分部的合同金额"))
    exact_date_pattern = r"(年|月)(的|)((\d|一|二|两|三|四|五|六|七|八|九|十)+)(号|日)"
    m = re.finditer(exact_date_pattern,"去年的15号广州分部的合同金额")
    for item in m:
        print(item.group(3))
